package com.mishow.application;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.mishow.utils.FileUtil;
import com.umeng.analytics.MobclickAgent;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Looper;


/**
 * 作者：wei.miao<br/>
 * 描述：UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录错误报告.
 */

public class CrashHandler implements UncaughtExceptionHandler {
	private Thread.UncaughtExceptionHandler mDefaultHandler;// 系统默认的UncaughtException处理类
	private static CrashHandler INSTANCE;// CrashHandler实例
	private Context mContext;// 程序的Context对象
	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");// 用于格式化日期,作为日志文件名的一部分

	private static WeakReference<Activity> lastActivityCreated = new WeakReference<>(null);

	/** 保证只有一个CrashHandler实例 */
	private CrashHandler() {

	}

	/** 获取CrashHandler实例 ,单例模式 */
	public static CrashHandler getInstance() {
		if (INSTANCE == null)
			INSTANCE = new CrashHandler();
		return INSTANCE;
	}

	/**
	 * 初始化
	 * 
	 * @param context
	 */
	@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
	public void init(Context context) {
		mContext = context;

		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器
		// We define a default exception handler that does what we want so it
		// can be called from Crashlytics/ACRA
		Thread.setDefaultUncaughtExceptionHandler(this);
		try {
			if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
				Application application = (Application) context.getApplicationContext();
				application.registerActivityLifecycleCallbacks(new MyActivityLifecycleCallbacks());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 当UncaughtException发生时会转入该重写的方法来处理
	 */
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果自定义的没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		}
	}

	/**
	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
	 * 
	 * @param ex
	 *            异常信息
	 * @return true 如果处理了该异常信息;否则返回false.
	 */
	public boolean handleException(final Throwable ex) {
		if (ex == null || mContext == null)
			return false;
		final String crashReport = getCrashReport(mContext, ex);

		ex.printStackTrace();

		new Thread() {
			public void run() {
				try {
					File file = save2File(ex, crashReport);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

		}.start();
		killProcess();
		return true;
	}
	
	/**
	 * 退出程序
	 */
	private void killProcess() {
		Activity lastActivity = lastActivityCreated.get();
		if (lastActivity != null) {
			lastActivity.finish();
			lastActivityCreated.clear();
		}
		// 退出
		android.os.Process.killProcess(android.os.Process.myPid());
		System.exit(1);
		MobclickAgent.onKillProcess(mContext);
	}

	/**
	 * 将错误日志记录到sd卡 <br/>
	 * 
	 * @param [参数1]-[参数1说明]
	 *            <br/>
	 * @param [参数2]-[参数2说明]
	 *            <br/>
	 */
	private File save2File(Throwable ex, String crashReport) {
		String fileName = "crash-" + format.format(new Date()) + ".txt";
		if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
			try {
				File file = new File(FileUtil.getStructureDirs(FileUtil.CRASH_PATH), fileName);
				PrintWriter pw = new PrintWriter(file);
				pw.write(getVersionString(mContext));
				ex.printStackTrace(pw);
				pw.close();
				return file;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return null;
	}

	/**
	 * 获取APP崩溃异常报告
	 * 
	 * @param ex
	 * @return
	 */
	private String getCrashReport(Context context, Throwable ex) {
		StringBuffer exceptionStr = new StringBuffer();
		exceptionStr.append(getVersionString(context));
		exceptionStr.append("Exception: " + ex.getMessage() + "\n");
		StackTraceElement[] elements = ex.getStackTrace();
		for (int i = 0; i < elements.length; i++) {
			exceptionStr.append(elements[i].toString() + "\n");
		}
		return exceptionStr.toString();
	}

	/**
	 * 获取APP版本信息字符串
	 * 
	 * @param ex
	 * @return
	 */
	private String getVersionString(Context context) {
		PackageInfo pinfo = getPackageInfo(context);
		StringBuffer exceptionStr = new StringBuffer();
		exceptionStr.append("Version: " + pinfo.versionName + "(" + pinfo.versionCode + ")\n");
		exceptionStr.append("Android: " + android.os.Build.VERSION.RELEASE + "(" + android.os.Build.MODEL + ")\n");

		return exceptionStr.toString();
	}

	/**
	 * 获取App安装包信息
	 * 
	 * @return
	 */
	private PackageInfo getPackageInfo(Context context) {
		PackageInfo info = null;
		try {
			info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
		} catch (NameNotFoundException e) {
			// e.printStackTrace(System.err);
			// L.i("getPackageInfo err = " + e.getMessage());
		}
		if (info == null)
			info = new PackageInfo();
		return info;
	}

	@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
	private static class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
		int currentlyStartedActivities = 0;

		@Override
		public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
			lastActivityCreated = new WeakReference<>(activity);
		}

		@Override
		public void onActivityStarted(Activity activity) {
			currentlyStartedActivities++;
		}

		@Override
		public void onActivityResumed(Activity activity) {
			// Do nothing
		}

		@Override
		public void onActivityPaused(Activity activity) {
			// Do nothing
		}

		@Override
		public void onActivityStopped(Activity activity) {
			// Do nothing
			currentlyStartedActivities--;
		}

		@Override
		public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
			// Do nothing
		}

		@Override
		public void onActivityDestroyed(Activity activity) {
			// Do nothing
		}

	}
}